package controllers

import (
	"net/http"
	"time"

	"fmt"
	"strconv"

	"gitee.com/golanglearn/small/app"
	"gitee.com/golanglearn/small/models"
	"gitee.com/golanglearn/small/repositories"
	"gitee.com/golanglearn/small/services"

	"github.com/gorilla/mux"
)

type SceneController struct {
	*app.App
	repositories.SceneRepository
	repositories.UserRepository
	repositories.TaskRepository
}

type ScenePaginator struct {
	Total        int     `json:"total"`
	PerPage      int     `json:"perPage"`
	CurrentPage  int     `json:"currentPage"`
	LastPage     int     `json:"lastPage"`
	From         int     `json:"from"`
	To           int     `json:"to"`
	FirstPageUrl string  `json:"firstPageUrl"`
	LastPageUrl  string  `json:"lastPageUrl"`
	NextPageUrl  *string `json:"nextPageUrl"`
	PrevPageUrl  *string `json:"prevPageUrl"`
}

func NewSceneController(a *app.App, sr repositories.SceneRepository, ur repositories.UserRepository, tr repositories.TaskRepository) *SceneController {
	return &SceneController{a, sr, ur, tr}
}

func (sc *SceneController) GetAll(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	tid, err := strconv.Atoi(vars["tid"])
	if err != nil {
		NewAPIError(&APIError{false, "Invalid request", http.StatusBadRequest}, w)
		return
	}
	scenes, err := sc.SceneRepository.GetAll(tid)
	if err != nil {
		// something went wrong
		NewAPIError(&APIError{false, "Could not fetch scenes", http.StatusBadRequest}, w)
		return
	}

	NewAPIResponse(&APIResponse{Success: true, Data: scenes}, w, http.StatusOK)
}

func (sc *SceneController) GetAllPaging(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	tid, err := strconv.Atoi(vars["tid"])
	if err != nil {
		NewAPIError(&APIError{false, "Invalid request", http.StatusBadRequest}, w)
		return
	}
	httpScheme := "https://"
	total, _ := sc.SceneRepository.GetTotalSceneCount(tid)
	page := r.URL.Query().Get("page")
	pageInt, err := strconv.Atoi(page)
	if err != nil {
		pageInt = 1
	}
	perPage := r.URL.Query().Get("perpage")
	perPageInt, err := strconv.Atoi(perPage)
	if err != nil || perPageInt < 1 || perPageInt > 100 {
		perPageInt = 10
	}
	offset := (pageInt - 1) * perPageInt
	to := pageInt * perPageInt
	if to > total {
		to = total
	}

	from := offset + 1
	totalPages := (total-1)/perPageInt + 1
	prevPage := pageInt - 1
	firstPageUrl := fmt.Sprintf(httpScheme+r.Host+r.URL.Path+"?page=%d", 1)
	lastPageString := fmt.Sprintf(httpScheme+r.Host+r.URL.Path+"?page=%d", totalPages)
	var prevPageUrl string
	var nextPageUrl string
	if prevPage > 0 && prevPage < totalPages {
		prevPageUrl = fmt.Sprintf(httpScheme+r.Host+r.URL.Path+"?page=%d", prevPage)
	}

	nextPage := pageInt + 1
	if nextPage <= totalPages {
		nextPageUrl = fmt.Sprintf(httpScheme+r.Host+r.URL.Path+"?page=%d", nextPage)
	}

	scenes, err := sc.SceneRepository.Paginate(tid, perPageInt, offset)
	if err != nil {
		NewAPIError(&APIError{false, "Could not fetch scene", http.StatusBadRequest}, w)
		return
	}

	if len(scenes) == 0 {
		NewAPIResponse(&APIResponse{Success: false, Message: "Could not find scene", Data: scenes}, w, http.StatusNotFound)
		return
	}

	themePaginator := APIPagination{
		total,
		perPageInt,
		pageInt,
		totalPages,
		from,
		to,
		firstPageUrl,
		lastPageString,
		nextPageUrl,
		prevPageUrl,
	}

	NewAPIResponse(&APIResponse{Success: true, Data: scenes, Pagination: &themePaginator}, w, http.StatusOK)
}

func (sc *SceneController) Create(w http.ResponseWriter, r *http.Request) {
	uid, err := services.UserIdFromContext(r.Context())
	if err != nil {
		NewAPIError(&APIError{false, "Something went wrong", http.StatusInternalServerError}, w)
		return
	}

	vars := mux.Vars(r)
	tid, err := strconv.Atoi(vars["tid"])
	if err != nil {
		NewAPIError(&APIError{false, "Invalid request", http.StatusBadRequest}, w)
		return
	}

	scene, err := sc.SceneRepository.FindById(uid, tid)
	if scene != nil {
		NewAPIError(&APIError{false, "Only once", http.StatusBadRequest}, w)
		return
	}

	scene = &models.Scene{
		TID:       tid,
		UID:       uid,
		CreatedAt: time.Now(),
	}

	err = sc.SceneRepository.Create(scene)
	if err != nil {
		NewAPIError(&APIError{false, "Could not create scene", http.StatusBadRequest}, w)
		return
	}

	// TODO: Change this maybe put the user object into a context and get the author from there.
	u, err := sc.UserRepository.FindById(uid)
	if err != nil {
		NewAPIError(&APIError{false, "Content is required", http.StatusBadRequest}, w)
		return
	}
	scene.Name = u.Name

	defer r.Body.Close()
	NewAPIResponse(&APIResponse{Success: true, Message: "Scene created", Data: scene}, w, http.StatusOK)
}

func (sc *SceneController) Delete(w http.ResponseWriter, r *http.Request) {
	uid, err := services.UserIdFromContext(r.Context())
	if err != nil {
		NewAPIError(&APIError{false, "Something went wrong", http.StatusInternalServerError}, w)
		return
	}

	vars := mux.Vars(r)
	tid, err := strconv.Atoi(vars["tid"])
	if err != nil {
		NewAPIError(&APIError{false, "Invalid request", http.StatusBadRequest}, w)
		return
	}

	task, err := sc.TaskRepository.FindByTid(tid)
	if err == nil && task != nil {
		NewAPIError(&APIError{false, "Could not delete scene, the task has been generated", http.StatusBadRequest}, w)
		return
	}

	scene, err := sc.SceneRepository.FindById(uid, tid)
	if err == nil && uid != scene.UID {
		NewAPIError(&APIError{false, "Could not delete scene, Participants must be himself", http.StatusBadRequest}, w)
		return
	}

	err = sc.SceneRepository.Delete(uid, tid)
	if err != nil {
		NewAPIError(&APIError{false, "Could not delete scene", http.StatusBadRequest}, w)
		return
	}

	NewAPIResponse(&APIResponse{Success: true, Message: "Scene deleted", Data: scene}, w, http.StatusOK)
}
